package cn.cas.mango.util.dbscanHot;

import cn.cas.mango.util.ThreadUtil.ThreadPoolUtils;
import org.elasticsearch.common.collect.Tuple;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
public class GenerateAllNeighbors {
  private List<Data> allData;
  private Map<String, List<Data>> map;

  private double e = 0.5;
  private GenerateAllNeighbors(double e) {
    this.e = e;
    map = new ConcurrentHashMap<>();
  }
  public GenerateAllNeighbors(List<Data> allData, double e) {
    this(e);
    this.allData = allData;
    for (Data allDatum : allData) {
      map.put(allDatum.getId(), new ArrayList<>());
    }
  }

public GenerateAllNeighbors() {}
  private void addToMap(Data data, int comparing) {
    Data comparingData = allData.get(comparing);
    if (data.calSim(comparingData) >= e) {
      String id = data.getId();
      map.get(id).add(comparingData);
    }
  }

  /**
   *
   * @param start 分割位置的起点（包含）
   * @param end 分割位置的终点（包含）
   * @param splitNum 要分成多少组
   * @return 包含splitNum组的间隔序列
   */
  private List<Tuple<Integer, Integer>> makeBatch(int start, int end, int splitNum) {
    List<Tuple<Integer, Integer>> ret = new ArrayList<>();
    int range = (end - start + 1) / splitNum;
    for (int i = 0 ; i < splitNum; i++) {
      ret.add(Tuple.tuple(i * range, i * range + range));
    }
    int left = end - start + 1 - range * splitNum;
    if (left != 0) {
      ret.add(Tuple.tuple(end + 1 - left, end + 1));
    }
    return ret;
  }

  public Map<String, List<Data>> getAll() {
    int cpuNum = Runtime.getRuntime().availableProcessors();
    int coreSize = cpuNum * 2;

    List<Tuple<Integer, Integer>> batches = makeBatch(0, allData.size() - 1, coreSize - 1);
    CountDownLatch startGate = new CountDownLatch(batches.size());
    for (Tuple<Integer, Integer> batch: batches) {
      ThreadPoolUtils.execute(() -> getAllItem(batch.v1(), batch.v2(), startGate));
    }
    try {
      startGate.await();
    } catch (InterruptedException ex) {
      throw new RuntimeException(ex);
    }
    return map;
  }

  /**
   *
   * @param start 计算邻居的开始（包含）
   * @param end 集散邻居的结束（不包含）
   * @param startGate 计数变量
   */
  private void getAllItem(int start, int end, CountDownLatch startGate) {

    for (int i = start; i < end; i++) {
      Data data = allData.get(i);
//      for (int j = Math.max(i - range, 0); j < (Math.min(i + range, allData.size())); j++) {
      for(int j = 0; j < allData.size(); j++) {
        if (j == i) continue;
        addToMap(data, j);
      }
    }
    startGate.countDown();
  }
}
